/*
 * Module:	Date operation library
 * Author:	Lvjianfeng
 * Date:	2018.10
 */


#include "lib_date.h"


/* for description of this algorithm see
 * http://howardhinnant.github.io/date_algorithms.html */

//Constant definition

/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
#define DAYS_PER_ERA				146097
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
#define DAYS_PER_CENTURY			36524
/* there is one leap year every 4 years */
#define DAYS_PER_4_YEARS			(3 * 365 + 366)
/* number of days in a non-leap year */
#define DAYS_PER_YEAR				365
/* number of years per era */
#define YEARS_PER_ERA				400

#define MONTH_FIRST					3
#define MONTHS_PER_YEAR				12
#define DAYS_OFFSET					60


//Type definition


//Private variable definition


//Private function declaration


//Public function definition

uint LibDate_DateToDays
(
	sint32 *s32p_Days,
	sint16 s16_Year,
	uint ui_Month,
	uint ui_Day
)
{
	sint16 s16_Era;
	uint32 u32_YearOfEra;
	uint32 u32_DayOfYear;
	uint32 u32_DayOfEra;


	if (ui_Month < MONTH_FIRST)
	{
		s16_Year--;
	}

	s16_Era = s16_Year;

	if (s16_Year < 0)
	{
		s16_Era = s16_Year - (YEARS_PER_ERA - 1);
	}

	s16_Era /= YEARS_PER_ERA;
	u32_YearOfEra = (uint32)(s16_Year - s16_Era * YEARS_PER_ERA);

	if (ui_Month >= MONTH_FIRST)
	{
		ui_Month -= MONTH_FIRST;
	}
	else
	{
		ui_Month += MONTHS_PER_YEAR - MONTH_FIRST;
	}

	u32_DayOfYear = (153 * (uint32)ui_Month + 2) / 5 + (uint32)ui_Day - 1;
	u32_DayOfEra = u32_YearOfEra * DAYS_PER_YEAR + u32_YearOfEra / 4 - 
		u32_YearOfEra / 100 + u32_DayOfYear;
	*s32p_Days = (sint32)s16_Era * DAYS_PER_ERA + (sint32)u32_DayOfEra +
		DAYS_OFFSET;

	return FUNCTION_OK;
}


uint LibDate_DaysToDate
(
	sint32 s32_Days,
	sint16 *s16p_Year,
	uint *uip_Month,
	uint *uip_Day
)
{
	sint32 s32_Era;
	uint32 u32_YearOfEra;
	uint32 u32_DayOfYear;
	uint32 u32_DayOfEra;
	uint32 u32_Month;


	s32_Days -= DAYS_OFFSET;
	s32_Era = s32_Days;

	if (s32_Days < 0)
	{
		s32_Era -= DAYS_PER_ERA - 1;
	}

	s32_Era /= DAYS_PER_ERA;
	u32_DayOfEra = (uint32)(s32_Days - s32_Era * DAYS_PER_ERA);
	u32_YearOfEra = (u32_DayOfEra - u32_DayOfEra / (DAYS_PER_4_YEARS - 1) +
		u32_DayOfEra / DAYS_PER_CENTURY - u32_DayOfEra / (DAYS_PER_ERA - 1)) /
		DAYS_PER_YEAR;
	u32_DayOfYear = u32_DayOfEra - (DAYS_PER_YEAR * u32_YearOfEra + 
		u32_YearOfEra / 4 - u32_YearOfEra / 100);
	u32_Month = (5 * u32_DayOfYear + 2) / 153;
	*uip_Day = (uint)(u32_DayOfYear - (153 * u32_Month + 2) / 5 + 1);

	if (u32_Month <= MONTHS_PER_YEAR - MONTH_FIRST)
	{
		u32_Month += MONTH_FIRST;	
	}
	else
	{
		u32_Month -= MONTHS_PER_YEAR - MONTH_FIRST;
	}

	*uip_Month = (uint)u32_Month;
	*s16p_Year = (sint16)u32_YearOfEra + (sint16)s32_Era * YEARS_PER_ERA;

	if (u32_Month < MONTH_FIRST)
	{
		*s16p_Year = *s16p_Year + 1;
	}

	return FUNCTION_OK;
}
